package br.mikhas.util;

/**
 * Byte convertions
 * 
 * @author Mikhail Domanoski
 * 
 */
public final class Bytes {

	/**
	 * <tt>boolean</tt> length in bytes
	 */
	public final static int BOOLEAN_LENGTH = 1;

	/**
	 * <tt>int</tt> length in bytes
	 */
	public final static int INT_LENGTH = 4;

	/**
	 * <tt>short</tt> length in bytes
	 */
	public final static int SHORT_LENGTH = 2;

	/**
	 * <tt>char</tt> length in bytes
	 */
	public final static int CHAR_LENGTH = SHORT_LENGTH;

	/**
	 * <tt>long</tt> length in bytes
	 */
	public final static int LONG_LENGTH = 8;

	/**
	 * <tt>float</tt> length in bytes
	 */
	public final static int FLOAT_LENGTH = INT_LENGTH;

	/**
	 * <tt>double</tt> length in bytes
	 */
	public final static int DOUBLE_LENGTH = LONG_LENGTH;

	private Bytes() {

	}

	/**
	 * Gets the primitive length in bytes
	 * 
	 * @param type
	 *            A java primitive type
	 * @return The primitive type length in bytes on -1 if it is not a primitive
	 */
	public static int getLength(Class<?> type) {
		if (type == int.class || type == float.class)
			return INT_LENGTH;
		else if (type == short.class || type == char.class)
			return SHORT_LENGTH;
		else if (type == long.class || type == double.class)
			return LONG_LENGTH;
		else if (type == boolean.class)
			return BOOLEAN_LENGTH;
		return -1;
	}

	/**
	 * Converts and appends an integer into a array of 4 bytes
	 * 
	 * @param integer
	 *            To be converted
	 * @param bytes
	 *            The byte array
	 * @param offset
	 *            The byte array index which the int will be appended
	 * @throws ArrayIndexOutOfBoundsException
	 *             if the array has not enought length
	 */
	public static void get(int integer, byte[] bytes, int offset) {
		bytes[offset + 0] = (byte) ((integer >>> 24) & 0xFF);
		bytes[offset + 1] = (byte) ((integer >>> 16) & 0xFF);
		bytes[offset + 2] = (byte) ((integer >>> 8) & 0xFF);
		bytes[offset + 3] = (byte) ((integer >>> 0) & 0xFF);
	}

	/**
	 * Converts an integer into a array of 4 bytes
	 * 
	 * @param integer
	 *            To be converted
	 * @return A 4 bytes length byte array
	 */
	public static byte[] get(int integer) {
		byte[] bytes = new byte[INT_LENGTH];
		get(integer, bytes, 0);
		return bytes;
	}

	/**
	 * Converts and appends a short into a array of 2 bytes
	 * 
	 * @param s
	 *            The short value to be converted
	 * @param bytes
	 *            The byte array
	 * @param offset
	 *            The index of the array where the bytes will be appended
	 * @throws ArrayIndexOutOfBoundsException
	 *             if the array has not enought length
	 */
	public static void get(short s, byte[] bytes, int offset) {
		bytes[offset + 0] = (byte) ((s >>> 8) & 0xFF);
		bytes[offset + 1] = (byte) ((s >>> 0) & 0xFF);
	}

	/**
	 * Converts a short into a array of 2 bytes
	 * 
	 * @param s
	 *            The short value to be converted
	 * @return A 2 bytes length byte array
	 */
	public static byte[] get(short s) {
		byte[] bytes = new byte[SHORT_LENGTH];
		get(s, bytes, 0);
		return bytes;
	}

	/**
	 * Converts a char into a array of 2 bytes
	 * 
	 * @param s
	 *            The char value to be converted
	 * @return A 2 bytes length array
	 */
	public static void get(char c, byte[] bytes, int offset) {
		get((short) c, bytes, offset);
	}

	/**
	 * Converts a char into a array of 2 bytes
	 * 
	 * @param s
	 *            The char value to be converted
	 * @return A 2 bytes length array
	 */
	public static byte[] get(char c) {
		return get((short) c);
	}

	/**
	 * Converts a <code>long</code> to an 8 bytes length array
	 * 
	 * @param l
	 *            The <code>long</code> value to be convert
	 * @return A 8 bytes length array with the byte representation of the long
	 *         number
	 */
	public static void get(long l, byte[] bytes, int offset) {
		bytes[offset + 0] = (byte) (l >>> 56);
		bytes[offset + 1] = (byte) (l >>> 48);
		bytes[offset + 2] = (byte) (l >>> 40);
		bytes[offset + 3] = (byte) (l >>> 32);
		bytes[offset + 4] = (byte) (l >>> 24);
		bytes[offset + 5] = (byte) (l >>> 16);
		bytes[offset + 6] = (byte) (l >>> 8);
		bytes[offset + 7] = (byte) (l >>> 0);
	}

	/**
	 * Converts a <code>long</code> to an 8 bytes length array
	 * 
	 * @param l
	 *            The <code>long</code> value to be convert
	 * @return A 8 bytes length array with the byte representation of the long
	 *         number
	 */
	public static byte[] get(long l) {
		byte[] bytes = new byte[LONG_LENGTH];
		get(l, bytes, 0);
		return bytes;
	}

	/**
	 * Converts a <code>double</code> to an 8 byte length array and appends it
	 * on an array.
	 * 
	 * @param d
	 *            The double number to be converted
	 * @param bytes
	 *            The byte array where the bytes will be appended
	 * @param offset
	 *            The index of the byte array where the double wil be appended
	 */
	public static void get(double d, byte[] bytes, int offset) {
		get(Double.doubleToLongBits(d), bytes, offset);
	}

	public static byte[] get(double d) {
		return get(Double.doubleToLongBits(d));
	}

	public static void get(float f, byte[] bytes, int offset) {
		get(Float.floatToIntBits(f), bytes, offset);
	}

	public static byte[] get(float f) {
		return get(Float.floatToIntBits(f));
	}

	/**
	 * Converts an array of bytes into an java integer
	 * 
	 * @param bytes
	 *            a 4 bytes length array that will be converted into a java
	 *            integer
	 * @return an valid java integer
	 */
	public static int toInt(byte[] bytes, int offset) {
		int i = 0;

		i += bytes[offset + 0] << 24;
		i += bytes[offset + 1] << 16;
		i += bytes[offset + 2] << 8;
		i += bytes[offset + 3] << 0;

		return i;
	}

	public static long toLong(byte[] bytes, int offset) {
		long f = 0;

		f += ((long) (bytes[offset + 0] & 0xFF) << 56);
		f += ((long) (bytes[offset + 1] & 0xFF) << 48);
		f += ((long) (bytes[offset + 2] & 0xFF) << 40);
		f += ((long) (bytes[offset + 3] & 0xFF) << 32);
		f += ((long) (bytes[offset + 4] & 0xFF) << 24);
		f += ((long) (bytes[offset + 5] & 0xFF) << 16);
		f += ((long) (bytes[offset + 6] & 0xFF) << 8);
		f += ((long) (bytes[offset + 7] & 0xFF) << 0);

		return f;
	}

	/**
	 * Converts an array of bytes into a java short
	 * 
	 * @param bytes
	 *            a 2 bytes length array that will be converted into a java
	 *            short
	 * @return an valid java short
	 */
	public static short toShort(byte[] bytes, int offset) {
		short s = 0;

		s += bytes[offset + 0] << 8;
		s += bytes[offset + 1] << 0;

		return s;
	}

	/**
	 * Converts an array of bytes into a java char
	 * 
	 * @param bytes
	 *            a 2 bytes length array that will be converted into a java char
	 * @return a valid java char
	 */
	public static char toChar(byte[] bytes, int offset) {
		return (char) toShort(bytes, offset);
	}

	public static float toFloat(byte[] bytes, int offset) {
		return Float.intBitsToFloat(toInt(bytes, offset));
	}

	public static double toDouble(byte[] bytes, int offset) {
		return Double.longBitsToDouble(toLong(bytes, offset));
	}
}